/*
 * DBCallableStatement.java
 *
 * Created on July 16, 2007, 5:14 PM
 *
 */

package com.absisgroup.nsc.utilities;

import java.io.InputStream;
import java.io.Reader;
import java.math.BigDecimal;
import java.net.URL;
import java.sql.*;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Hashtable;
import java.util.Map;

/**
 *
 * @author hue
 */
public class DBCallableStatement implements CallableStatement {
    private boolean bKeepParams;
    private long sqlId;
    private Hashtable htParams = new Hashtable();
    private String strQuery;
    private CallableStatement stmt;
    private String logFile;
    private java.util.Date startTime;
    private String outParamIndexes = "|";
    private String sessionId;
    
    /** Creates a new instance of DBCallableStatement */
    public DBCallableStatement(final CallableStatement _stmt, final String _strQuery, final String _sqlLogfile, final String _sessionId) {
        stmt = _stmt;
        strQuery = _strQuery;
        logFile = _sqlLogfile;
        bKeepParams = (logFile != null);
        sessionId = _sessionId;
        sqlId = ++DBConnection.sqlId;
    }

    @Override
    public void registerOutParameter(final int parameterIndex, final int sqlType) throws SQLException {
        outParamIndexes += parameterIndex + "|";
        stmt.registerOutParameter(parameterIndex, sqlType);
    }

    @Override
    public void registerOutParameter(final int parameterIndex, final int sqlType, final int scale) throws SQLException {
        outParamIndexes += parameterIndex + "|";
        stmt.registerOutParameter(parameterIndex, sqlType, scale);
    }

    @Override
    public boolean wasNull() throws SQLException {
        return stmt.wasNull();
    }

    @Override
    public String getString(final int parameterIndex) throws SQLException {
        return stmt.getString(parameterIndex);
    }

    @Override
    public boolean getBoolean(final int parameterIndex) throws SQLException {
        return stmt.getBoolean(parameterIndex);
    }

    @Override
    public byte getByte(final int parameterIndex) throws SQLException {
        return stmt.getByte(parameterIndex);
    }

    @Override
    public short getShort(final int parameterIndex) throws SQLException {
        return stmt.getShort(parameterIndex);
    }

    @Override
    public int getInt(final int parameterIndex) throws SQLException {
        return stmt.getInt(parameterIndex);
    }

    @Override
    public long getLong(final int parameterIndex) throws SQLException {
        return stmt.getLong(parameterIndex);
    }

    @Override
    public float getFloat(final int parameterIndex) throws SQLException {
        return stmt.getFloat(parameterIndex);
    }

    @Override
    public double getDouble(final int parameterIndex) throws SQLException {
        return stmt.getDouble(parameterIndex);
    }

    @Override
    public BigDecimal getBigDecimal(final int parameterIndex, final int scale) throws SQLException {
        return stmt.getBigDecimal(parameterIndex, scale);
    }

    @Override
    public byte[] getBytes(final int parameterIndex) throws SQLException {
        return stmt.getBytes(parameterIndex);
    }

    @Override
    public Date getDate(final int parameterIndex) throws SQLException {
        return stmt.getDate(parameterIndex);
    }

    @Override
    public Time getTime(final int parameterIndex) throws SQLException {
        return stmt.getTime(parameterIndex);
    }

    @Override
    public Timestamp getTimestamp(final int parameterIndex) throws SQLException {
        return stmt.getTimestamp(parameterIndex);
    }

    @Override
    public Object getObject(final int parameterIndex) throws SQLException {
        return stmt.getObject(parameterIndex);
    }

    @Override
    public BigDecimal getBigDecimal(final int parameterIndex) throws SQLException {
        return stmt.getBigDecimal(parameterIndex);
    }

    @Override
    public Object getObject(final int i, final Map map) throws SQLException {
        return stmt.getObject(i, map);
    }

    @Override
    public Ref getRef(final int i) throws SQLException {
        return stmt.getRef(i);
    }

    @Override
    public Blob getBlob(final int i) throws SQLException {
        return stmt.getBlob(i);
    }

    @Override
    public Clob getClob(final int i) throws SQLException {
        return stmt.getClob(i);
    }

    @Override
    public Array getArray(final int i) throws SQLException {
        return stmt.getArray(i);
    }

    @Override
    public Date getDate(final int parameterIndex, final Calendar cal) throws SQLException {
        return stmt.getDate(parameterIndex, cal);
    }

    @Override
    public Time getTime(final int parameterIndex, final Calendar cal) throws SQLException {
        return stmt.getTime(parameterIndex, cal);
    }

    @Override
    public Timestamp getTimestamp(final int parameterIndex, final Calendar cal) throws SQLException {
        return stmt.getTimestamp(parameterIndex, cal);
    }

    @Override
    public void registerOutParameter(final int paramIndex, final int sqlType, final String typeName) throws SQLException {
        outParamIndexes += paramIndex + "|";
        stmt.registerOutParameter(paramIndex, sqlType, typeName);
    }

    @Override
    public void registerOutParameter(final String parameterName, final int sqlType) throws SQLException {
        stmt.registerOutParameter(parameterName, sqlType);
    }

    @Override
    public void registerOutParameter(final String parameterName, final int sqlType, final int scale) throws SQLException {
        stmt.registerOutParameter(parameterName, sqlType, scale);
    }

    @Override
    public void registerOutParameter(final String parameterName, final int sqlType, final String typeName) throws SQLException {
        stmt.registerOutParameter(parameterName, sqlType, typeName);
    }

    @Override
    public URL getURL(final int parameterIndex) throws SQLException {
        return stmt.getURL(parameterIndex);
    }

    @Override
    public void setURL(final String parameterName, final URL val) throws SQLException {
        stmt.setURL(parameterName, val);
    }

    @Override
    public void setNull(final String parameterName, final int sqlType) throws SQLException {
        stmt.setNull(parameterName, sqlType);
    }

    @Override
    public void setBoolean(final String parameterName, final boolean x) throws SQLException {
        stmt.setBoolean(parameterName, x);
    }

    @Override
    public void setByte(final String parameterName, final byte x) throws SQLException {
        stmt.setByte(parameterName, x);
    }

    @Override
    public void setShort(final String parameterName, final short x) throws SQLException {
        stmt.setShort(parameterName, x);
    }

    @Override
    public void setInt(final String parameterName, final int x) throws SQLException {
        stmt.setInt(parameterName, x);
    }

    @Override
    public void setLong(final String parameterName, final long x) throws SQLException {
        stmt.setLong(parameterName, x);
    }

    @Override
    public void setFloat(final String parameterName, final float x) throws SQLException {
        stmt.setFloat(parameterName, x);
    }

    @Override
    public void setDouble(final String parameterName, final double x) throws SQLException {
        stmt.setDouble(parameterName, x);
    }

    @Override
    public void setBigDecimal(final String parameterName, final BigDecimal x) throws SQLException {
        stmt.setBigDecimal(parameterName, x);
    }

    @Override
    public void setString(final String parameterName, final String x) throws SQLException {
        stmt.setString(parameterName, x);
    }

    @Override
    public void setBytes(final String parameterName, final byte[] x) throws SQLException {
        stmt.setBytes(parameterName, x);
    }

    @Override
    public void setDate(final String parameterName, final Date x) throws SQLException {
        stmt.setDate(parameterName, x);
    }

    @Override
    public void setTime(final String parameterName, final Time x) throws SQLException {
        stmt.setTime(parameterName, x);
    }

    @Override
    public void setTimestamp(final String parameterName, final Timestamp x) throws SQLException {
        stmt.setTimestamp(parameterName, x);
    }

    @Override
    public void setAsciiStream(final String parameterName, final InputStream x, final int length) throws SQLException {
        stmt.setAsciiStream(parameterName, x, length);
    }

    @Override
    public void setBinaryStream(final String parameterName, final InputStream x, final int length) throws SQLException {
        stmt.setBinaryStream(parameterName, x, length);
    }

    @Override
    public void setObject(final String parameterName, final Object x, final int targetSqlType, final int scale) throws SQLException {
        stmt.setObject(parameterName, x, targetSqlType, scale);
    }

    @Override
    public void setObject(final String parameterName, final Object x, final int targetSqlType) throws SQLException {
        stmt.setObject(parameterName, x, targetSqlType);
    }

    @Override
    public void setObject(final String parameterName, final Object x) throws SQLException {
        stmt.setObject(parameterName, x);
    }

    @Override
    public void setCharacterStream(final String parameterName, final Reader reader, final int length) throws SQLException {
        stmt.setCharacterStream(parameterName, reader, length);
    }

    @Override
    public void setDate(final String parameterName, final Date x, final Calendar cal) throws SQLException {
        stmt.setDate(parameterName, x, cal);
    }

    @Override
    public void setTime(final String parameterName, final Time x, final Calendar cal) throws SQLException {
        stmt.setTime(parameterName, x, cal);
    }

    @Override
    public void setTimestamp(final String parameterName, final Timestamp x, final Calendar cal) throws SQLException {
        stmt.setTimestamp(parameterName, x, cal);
    }

    @Override
    public void setNull(final String parameterName, final int sqlType, final String typeName) throws SQLException {
        stmt.setNull(parameterName, sqlType, typeName);
    }

    @Override
    public String getString(final String parameterName) throws SQLException {
        return stmt.getString(parameterName);
    }

    @Override
    public boolean getBoolean(final String parameterName) throws SQLException {
        return stmt.getBoolean(parameterName);
    }

    @Override
    public byte getByte(final String parameterName) throws SQLException {
        return stmt.getByte(parameterName);
    }

    @Override
    public short getShort(final String parameterName) throws SQLException {
        return stmt.getShort(parameterName);
    }

    @Override
    public int getInt(final String parameterName) throws SQLException {
        return stmt.getInt(parameterName);
    }

    @Override
    public long getLong(final String parameterName) throws SQLException {
        return stmt.getLong(parameterName);
    }

    @Override
    public float getFloat(final String parameterName) throws SQLException {
        return stmt.getFloat(parameterName);
    }

    @Override
    public double getDouble(final String parameterName) throws SQLException {
        return stmt.getDouble(parameterName);
    }

    @Override
    public byte[] getBytes(final String parameterName) throws SQLException {
        return stmt.getBytes(parameterName);
    }

    @Override
    public Date getDate(final String parameterName) throws SQLException {
        return stmt.getDate(parameterName);
    }

    @Override
    public Time getTime(final String parameterName) throws SQLException {
        return stmt.getTime(parameterName);
    }

    @Override
    public Timestamp getTimestamp(final String parameterName) throws SQLException {
        return stmt.getTimestamp(parameterName);
    }

    @Override
    public Object getObject(final String parameterName) throws SQLException {
        return stmt.getObject(parameterName);
    }

    @Override
    public BigDecimal getBigDecimal(final String parameterName) throws SQLException {
        return stmt.getBigDecimal(parameterName);
    }

    @Override
    public Object getObject(final String parameterName, final Map map) throws SQLException {
        return stmt.getObject(parameterName, map);
    }

    @Override
    public Ref getRef(final String parameterName) throws SQLException {
        return stmt.getRef(parameterName);
    }

    @Override
    public Blob getBlob(final String parameterName) throws SQLException {
        return stmt.getBlob(parameterName);
    }

    @Override
    public Clob getClob(final String parameterName) throws SQLException {
        return stmt.getClob(parameterName);
    }

    @Override
    public Array getArray(final String parameterName) throws SQLException {
        return stmt.getArray(parameterName);
    }

    @Override
    public Date getDate(final String parameterName, final Calendar cal) throws SQLException {
        return stmt.getDate(parameterName, cal);
    }

    @Override
    public Time getTime(final String parameterName, final Calendar cal) throws SQLException {
        return stmt.getTime(parameterName, cal);
    }

    @Override
    public Timestamp getTimestamp(final String parameterName, final Calendar cal) throws SQLException {
        return stmt.getTimestamp(parameterName, cal);
    }

    @Override
    public URL getURL(final String parameterName) throws SQLException {
        return stmt.getURL(parameterName);
    }

    @Override
    public ResultSet executeQuery() throws SQLException {
        try {
            saveSQLStatement();
            return stmt.executeQuery();
        }
        catch(java.sql.SQLException _ex) {
            final DBSQLException ex = new DBSQLException(_ex, getSQLStatement());
            //close the satement if there's an error
            this.close();
            throw ex;
        }
    }

    @Override
    public int executeUpdate() throws SQLException {
        try {
            saveSQLStatement();
            return stmt.executeUpdate();
        }
        catch(java.sql.SQLException _ex) {
            final DBSQLException ex = new DBSQLException(_ex, getSQLStatement());
            //close the satement if there's an error
            this.close();
            throw ex;
        }
    }

    @Override
    public void setNull(final int parameterIndex, final int sqlType) throws SQLException {
        stmt.setNull(parameterIndex, sqlType);
    }

    @Override
    public void setBoolean(final int parameterIndex, final boolean x) throws SQLException {
        keepParam(parameterIndex, x);
        stmt.setBoolean(parameterIndex, x);
    }

    @Override
    public void setByte(final int parameterIndex, final byte x) throws SQLException {
        keepParam(parameterIndex, new Byte(x));
        stmt.setByte(parameterIndex, x);
    }

    @Override
    public void setShort(final int parameterIndex, final short x) throws SQLException {
        keepParam(parameterIndex, new Short(x));
        stmt.setShort(parameterIndex, x);
    }

    @Override
    public void setInt(final int parameterIndex, final int x) throws SQLException {
        keepParam(parameterIndex, new Integer(x));
        stmt.setInt(parameterIndex, x);
    }

    @Override
    public void setLong(final int parameterIndex, final long x) throws SQLException {
        keepParam(parameterIndex, new Long(x));
        stmt.setLong(parameterIndex, x);
    }

    @Override
    public void setFloat(final int parameterIndex, final float x) throws SQLException {
        keepParam(parameterIndex, new Float(x));
        stmt.setFloat(parameterIndex, x);
    }

    @Override
    public void setDouble(final int parameterIndex, final double x) throws SQLException {
        keepParam(parameterIndex, new Double(x));
        stmt.setDouble(parameterIndex, x);
    }

    @Override
    public void setBigDecimal(final int parameterIndex, final BigDecimal x) throws SQLException {
        keepParam(parameterIndex, x);
        stmt.setBigDecimal(parameterIndex, x);
    }

    @Override
    public void setString(final int parameterIndex, final String x) throws SQLException {
        keepParam(parameterIndex, x);
        stmt.setString(parameterIndex, x);
    }

    @Override
    public void setBytes(final int parameterIndex, final byte[] x) throws SQLException {
        keepParam(parameterIndex, x);
        stmt.setBytes(parameterIndex, x);
    }

    @Override
    public void setDate(final int parameterIndex, final Date x) throws SQLException {
        keepParam(parameterIndex, x);
        stmt.setDate(parameterIndex, x);
    }

    @Override
    public void setTime(final int parameterIndex, final Time x) throws SQLException {
        keepParam(parameterIndex, x);
        stmt.setTime(parameterIndex, x);
    }

    @Override
    public void setTimestamp(final int parameterIndex, final Timestamp x) throws SQLException {
        keepParam(parameterIndex, x);
        stmt.setTimestamp(parameterIndex, x);
    }

    @Override
    public void setAsciiStream(final int parameterIndex, final InputStream x, final int length) throws SQLException {
        keepParam(parameterIndex, x);
        stmt.setAsciiStream(parameterIndex, x, length);
    }

    @Override
    public void setUnicodeStream(final int parameterIndex, final InputStream x, final int length) throws SQLException {
        keepParam(parameterIndex, x);
        stmt.setUnicodeStream(parameterIndex, x, length);
    }

    @Override
    public void setBinaryStream(final int parameterIndex, final InputStream x, final int length) throws SQLException {
        keepParam(parameterIndex, x);
        stmt.setBinaryStream(parameterIndex, x, length);
    }

    @Override
    public void clearParameters() throws SQLException {
        stmt.clearParameters();
    }

    @Override
    public void setObject(final int parameterIndex, final Object x, final int targetSqlType, final int scale) throws SQLException {
        keepParam(parameterIndex, x);
        stmt.setObject(parameterIndex, x, targetSqlType, scale);
    }

    @Override
    public void setObject(final int parameterIndex, final Object x, final int targetSqlType) throws SQLException {
        keepParam(parameterIndex, x);
        stmt.setObject(parameterIndex, x, targetSqlType);
    }

    @Override
    public void setObject(final int parameterIndex, final Object x) throws SQLException {
        keepParam(parameterIndex, x);
        stmt.setObject(parameterIndex, x);
    }

    @Override
    public boolean execute() throws SQLException {
        try {
            saveSQLStatement();
            return stmt.execute();
        }
        catch(java.sql.SQLException _ex) {
            final DBSQLException ex = new DBSQLException(_ex, getSQLStatement());
            //close the satement if there's an error
            this.close();
            throw ex;
        }
    }

    @Override
    public void addBatch() throws SQLException {
        stmt.addBatch();
    }

    @Override
    public void setCharacterStream(final int parameterIndex, final Reader reader, final int length) throws SQLException {
        keepParam(parameterIndex, reader);
        stmt.setCharacterStream(parameterIndex, reader, length);
    }

    @Override
    public void setRef(final int i, final Ref x) throws SQLException {
        keepParam(i, x);
        stmt.setRef(i, x);
    }

    @Override
    public void setBlob(final int i, final Blob x) throws SQLException {
        keepParam(i, x);
        stmt.setBlob(i, x);
    }

    @Override
    public void setClob(final int i, final Clob x) throws SQLException {
        keepParam(i, x);
        stmt.setClob(i, x);
    }

    @Override
    public void setArray(final int i, final Array x) throws SQLException {
        keepParam(i, x);
        stmt.setArray(i, x);
    }

    @Override
    public ResultSetMetaData getMetaData() throws SQLException {
        return stmt.getMetaData();
    }

    @Override
    public void setDate(final int parameterIndex, final Date x, final Calendar cal) throws SQLException {
        keepParam(parameterIndex, x);
        stmt.setDate(parameterIndex, x, cal);
    }

    @Override
    public void setTime(final int parameterIndex, final Time x, final Calendar cal) throws SQLException {
        keepParam(parameterIndex, x);
        stmt.setTime(parameterIndex, x, cal);
    }

    @Override
    public void setTimestamp(final int parameterIndex, final Timestamp x, final Calendar cal) throws SQLException {
        keepParam(parameterIndex, x);
        stmt.setTimestamp(parameterIndex, x, cal);
    }

    @Override
    public void setNull(final int paramIndex, final int sqlType, final String typeName) throws SQLException {
        stmt.setNull(paramIndex, sqlType, typeName);
    }

    @Override
    public void setURL(final int parameterIndex, final URL x) throws SQLException {
        keepParam(parameterIndex, x);
        stmt.setURL(parameterIndex, x);
    }

    @Override
    public ParameterMetaData getParameterMetaData() throws SQLException {
        return stmt.getParameterMetaData();
    }

    @Override
    public ResultSet executeQuery(final String sql) throws SQLException {
        try {
            strQuery = sql;
            saveSQLStatement();
            return stmt.executeQuery(sql);
        }
        catch(java.sql.SQLException _ex) {
            final DBSQLException ex = new DBSQLException(_ex, getSQLStatement());
            //close the satement if there's an error
            this.close();
            throw ex;
        }
    }

    @Override
    public int executeUpdate(final String sql) throws SQLException {
        try {
            strQuery = sql;
            saveSQLStatement();
            return stmt.executeUpdate(sql);
        }
        catch(java.sql.SQLException _ex) {
            final DBSQLException ex = new DBSQLException(_ex, getSQLStatement());
            //close the satement if there's an error
            this.close();
            throw ex;
        }
    }

    @Override
    public void close() throws SQLException {
        try {
            stmt.close();
        }
        finally {
            release();
        }
    }

    @Override
    public int getMaxFieldSize() throws SQLException {
        return stmt.getMaxFieldSize();
    }

    @Override
    public void setMaxFieldSize(final int max) throws SQLException {
        stmt.setMaxFieldSize(max);
    }

    @Override
    public int getMaxRows() throws SQLException {
        return stmt.getMaxRows();
    }

    @Override
    public void setMaxRows(final int max) throws SQLException {
        stmt.setMaxRows(max);
    }

    @Override
    public void setEscapeProcessing(final boolean enable) throws SQLException {
        stmt.setEscapeProcessing(enable);
    }

    @Override
    public int getQueryTimeout() throws SQLException {
        return stmt.getQueryTimeout();
    }

    @Override
    public void setQueryTimeout(final int seconds) throws SQLException {
        stmt.setQueryTimeout(seconds);
    }

    @Override
    public void cancel() throws SQLException {
        stmt.cancel();
    }

    @Override
    public SQLWarning getWarnings() throws SQLException {
        return stmt.getWarnings();
    }

    @Override
    public void clearWarnings() throws SQLException {
        stmt.clearWarnings();
    }

    @Override
    public void setCursorName(final String name) throws SQLException {
        stmt.setCursorName(name);
    }

    @Override
    public boolean execute(final String sql) throws SQLException {
        try {
            strQuery = sql;
            saveSQLStatement();
            return stmt.execute(sql);
        }
        catch(java.sql.SQLException _ex) {
            final DBSQLException ex = new DBSQLException(_ex, getSQLStatement());
            //close the satement if there's an error
            this.close();
            throw ex;
        }
    }

    @Override
    public ResultSet getResultSet() throws SQLException {
        return stmt.getResultSet();
    }

    @Override
    public int getUpdateCount() throws SQLException {
        return stmt.getUpdateCount();
    }

    @Override
    public boolean getMoreResults() throws SQLException {
        return stmt.getMoreResults();
    }

    @Override
    public void setFetchDirection(final int direction) throws SQLException {
        stmt.setFetchDirection(direction);
    }

    @Override
    public int getFetchDirection() throws SQLException {
        return stmt.getFetchDirection();
    }

    @Override
    public void setFetchSize(final int rows) throws SQLException {
        stmt.setFetchSize(rows);
    }

    @Override
    public int getFetchSize() throws SQLException {
        return stmt.getFetchSize();
    }

    @Override
    public int getResultSetConcurrency() throws SQLException {
        return stmt.getResultSetConcurrency();
    }

    @Override
    public int getResultSetType() throws SQLException {
        return stmt.getResultSetType();
    }

    @Override
    public void addBatch(final String sql) throws SQLException {
        stmt.addBatch(sql);
    }

    @Override
    public void clearBatch() throws SQLException {
        stmt.clearBatch();
    }

    @Override
    public int[] executeBatch() throws SQLException {
        try {
            saveSQLStatement();
            return stmt.executeBatch();
        }
        catch(java.sql.SQLException _ex) {
            final DBSQLException ex = new DBSQLException(_ex, getSQLStatement());
            //close the satement if there's an error
            this.close();
            throw ex;
        }
    }

    @Override
    public Connection getConnection() throws SQLException {
        return stmt.getConnection();
    }

    @Override
    public boolean getMoreResults(final int current) throws SQLException {
        return stmt.getMoreResults(current);
    }

    @Override
    public ResultSet getGeneratedKeys() throws SQLException {
        return stmt.getGeneratedKeys();
    }

    @Override
    public int executeUpdate(final String sql, final int autoGeneratedKeys) throws SQLException {
        return stmt.executeUpdate(sql, autoGeneratedKeys);
    }

    @Override
    public int executeUpdate(final String sql, final int[] columnIndexes) throws SQLException {
        try {
            strQuery = sql;
            saveSQLStatement();
            return stmt.executeUpdate(sql, columnIndexes);
        }
        catch(java.sql.SQLException _ex) {
            final DBSQLException ex = new DBSQLException(_ex, getSQLStatement());
            //close the satement if there's an error
            this.close();
            throw ex;
        }
    }

    @Override
    public int executeUpdate(final String sql, final String[] columnNames) throws SQLException {
        try {
            strQuery = sql;
            saveSQLStatement();
            return stmt.executeUpdate(sql, columnNames);
        }
        catch(java.sql.SQLException _ex) {
            final DBSQLException ex = new DBSQLException(_ex, getSQLStatement());
            //close the satement if there's an error
            this.close();
            throw ex;
        }
    }

    @Override
    public boolean execute(final String sql, final int autoGeneratedKeys) throws SQLException {
        try {
            strQuery = sql;
            saveSQLStatement();
            return stmt.execute(sql, autoGeneratedKeys);
        }
        catch(java.sql.SQLException _ex) {
            final DBSQLException ex = new DBSQLException(_ex, getSQLStatement());
            //close the satement if there's an error
            this.close();
            throw ex;
        }
    }

    @Override
    public boolean execute(final String sql, final int[] columnIndexes) throws SQLException {
        try {
            strQuery = sql;
            saveSQLStatement();
            return stmt.execute(sql, columnIndexes);
        }
        catch(java.sql.SQLException _ex) {
            final DBSQLException ex = new DBSQLException(_ex, getSQLStatement());
            //close the satement if there's an error
            this.close();
            throw ex;
        }
    }

    @Override
    public boolean execute(final String sql, final String[] columnNames) throws SQLException {
        try {
            strQuery = sql;
            saveSQLStatement();
            return stmt.execute(sql, columnNames);
        }
        catch(java.sql.SQLException _ex) {
            final DBSQLException ex = new DBSQLException(_ex, getSQLStatement());
            //close the satement if there's an error
            this.close();
            throw ex;
        }
    }

    @Override
    public int getResultSetHoldability() throws SQLException {
        return stmt.getResultSetHoldability();
    }
    
    protected void keepParam(final int parameterIndex, final Object x) throws java.sql.SQLException {
        if(bKeepParams && x != null) {
            htParams.put(String.valueOf(parameterIndex), x);
        }
    }
    
    private void saveSQLStatement() {
        if(!bKeepParams || logFile == null) {
            return;
        }
        startTime = new java.util.Date();
        try {
            final StringBuffer sb = new StringBuffer("\r\n-- [SQL_C] [");
            sb.append(sessionId);
            sb.append(",sqlId=");
            sb.append(sqlId);
            sb.append("][");
            sb.append(Util.toString(startTime, "dd/MM/yyyy HH:mm:ss"));
            sb.append("]\r\n");
            sb.append(getSQLStatement());
            Util.writeFile(logFile, sb.toString().getBytes(), true);
        }
        catch(Exception ex) {
            logFile = null;
        }
    }
    
    private String getSQLStatement() {
        final int callIndex = strQuery.toLowerCase().indexOf("call");
        final int minIndex = strQuery.indexOf('?') < callIndex ? 1 : 0;
        final ArrayList alIndexes = new ArrayList();
        final int length = strQuery.length();
        for(int i=0; i<length; i++) {
            if(strQuery.charAt(i) == '?') {
                alIndexes.add(String.valueOf(i));
            }
        }
        //
        StringBuffer sb = new StringBuffer(strQuery);
        for(int i=alIndexes.size()-1; i>=minIndex; i--) {
            //keep the question mark if it's an output parameter.
            if(outParamIndexes.length() > 1 && outParamIndexes.indexOf("|" + String.valueOf(i+1) + "|") != -1) {
                continue;
            }
            final Object value = htParams.get(String.valueOf(i+1));
            String strValue = "";
            if(value == null) {
                strValue = "null";
            }
            else if(value instanceof java.lang.String) {
                strValue = "'" + Util.replace((String)value, "'", "''") + "'";
            }
            else if(value instanceof java.util.Date) {
                final String dateString = Util.toString((java.util.Date)value ,"dd/MM/yyyy HH:mm:ss");
                strValue = "to_date('" + dateString + "', 'dd/mm/yyyy hh24:mi:ss')";
            }
            else {
                strValue = value.toString();
            }
            final int index = Integer.parseInt((String)alIndexes.get(i));
            sb = sb.replace(index, index+1, strValue);
        }
        String strTmp = sb.toString();
        strTmp = strTmp.trim();
        //remove the characters '{' and '}'
        strTmp = strTmp.substring(1, strTmp.length() - 1);
        strTmp = strTmp.trim() + ";";
        //replace 'call' by 'exec'
        final int iIndex = strTmp.toLowerCase().indexOf("call");
        if(iIndex != -1) {
            strTmp = new StringBuffer(strTmp).replace(iIndex, iIndex + 4, "exec").toString();
        }
        return strTmp;
    }
    
    private void saveExecutionTime() {
        if(!bKeepParams || startTime == null || logFile == null) {
            return;
        }
        try {
            final StringBuffer sb = new StringBuffer("\r\n-- [SQL_R] [");
            sb.append(sessionId);
            sb.append(",sqlId=");
            sb.append(sqlId);
            sb.append("][");
            sb.append(Util.calculateDuration(startTime, new java.util.Date()));
            sb.append(']');
            Util.writeFile(logFile, sb.toString().getBytes(), true);
        }
        catch(Exception ex) {
            logFile = null;
        }
    }
    
    private void release() {
        try {
            stmt = null;
            htParams.clear();
            htParams = null;
        }
        catch (Exception ex) {
            System.out.println(ex);
        }
        saveExecutionTime();
    }

    @Override
    public RowId getRowId(int parameterIndex) throws SQLException {
        return stmt.getRowId(parameterIndex);
    }

    @Override
    public RowId getRowId(String parameterName) throws SQLException {
        return stmt.getRowId(parameterName);
    }

    @Override
    public void setRowId(String parameterName, RowId x) throws SQLException {
        stmt.setRowId(parameterName, x);
    }

    @Override
    public void setNString(String parameterName, String value) throws SQLException {
        stmt.setNString(parameterName, value);
    }

    @Override
    public void setNCharacterStream(String parameterName, Reader value, long length) throws SQLException {
        stmt.setNCharacterStream(parameterName, value, length);
    }

    @Override
    public void setNClob(String parameterName, NClob value) throws SQLException {
        stmt.setNClob(parameterName, value);
    }

    @Override
    public void setClob(String parameterName, Reader reader, long length) throws SQLException {
        stmt.setClob(parameterName, reader, length);
    }

    @Override
    public void setBlob(String parameterName, InputStream inputStream, long length) throws SQLException {
        stmt.setBlob(parameterName, inputStream, length);
    }

    @Override
    public void setNClob(String parameterName, Reader reader, long length) throws SQLException {
        stmt.setNClob(parameterName, reader, length);
    }

    @Override
    public NClob getNClob(int parameterIndex) throws SQLException {
        return stmt.getNClob(parameterIndex);
    }

    @Override
    public NClob getNClob(String parameterName) throws SQLException {
        return stmt.getNClob(parameterName);
    }

    @Override
    public void setSQLXML(String parameterName, SQLXML xmlObject) throws SQLException {
        stmt.setSQLXML(parameterName, xmlObject);
    }

    @Override
    public SQLXML getSQLXML(int parameterIndex) throws SQLException {
        return stmt.getSQLXML(parameterIndex);
    }

    @Override
    public SQLXML getSQLXML(String parameterName) throws SQLException {
        return stmt.getSQLXML(parameterName);
    }

    @Override
    public String getNString(int parameterIndex) throws SQLException {
        return stmt.getNString(parameterIndex);
    }

    @Override
    public String getNString(String parameterName) throws SQLException {
        return stmt.getNString(parameterName);
    }

    @Override
    public Reader getNCharacterStream(int parameterIndex) throws SQLException {
        return stmt.getNCharacterStream(parameterIndex);
    }

    @Override
    public Reader getNCharacterStream(String parameterName) throws SQLException {
        return stmt.getNCharacterStream(parameterName);
    }

    @Override
    public Reader getCharacterStream(int parameterIndex) throws SQLException {
        return stmt.getCharacterStream(parameterIndex);
    }

    @Override
    public Reader getCharacterStream(String parameterName) throws SQLException {
        return stmt.getCharacterStream(parameterName);
    }

    @Override
    public void setBlob(String parameterName, Blob x) throws SQLException {
        stmt.setBlob(parameterName, x);
    }

    @Override
    public void setClob(String parameterName, Clob x) throws SQLException {
        stmt.setClob(parameterName, x);
    }

    @Override
    public void setAsciiStream(String parameterName, InputStream x, long length) throws SQLException {
        stmt.setAsciiStream(parameterName, x, length);
    }

    @Override
    public void setBinaryStream(String parameterName, InputStream x, long length) throws SQLException {
        stmt.setBinaryStream(parameterName, x, length);
    }

    @Override
    public void setCharacterStream(String parameterName, Reader reader, long length) throws SQLException {
        stmt.setCharacterStream(parameterName, reader, length);
    }

    @Override
    public void setAsciiStream(String parameterName, InputStream x) throws SQLException {
        stmt.setAsciiStream(parameterName, x);
    }

    @Override
    public void setBinaryStream(String parameterName, InputStream x) throws SQLException {
        stmt.setBinaryStream(parameterName, x);
    }

    @Override
    public void setCharacterStream(String parameterName, Reader reader) throws SQLException {
        stmt.setCharacterStream(parameterName, reader);
    }

    @Override
    public void setNCharacterStream(String parameterName, Reader value) throws SQLException {
        stmt.setNCharacterStream(parameterName, value);
    }

    @Override
    public void setClob(String parameterName, Reader reader) throws SQLException {
        stmt.setClob(parameterName, reader);
    }

    @Override
    public void setBlob(String parameterName, InputStream inputStream) throws SQLException {
        stmt.setBlob(parameterName, inputStream);
    }

    @Override
    public void setNClob(String parameterName, Reader reader) throws SQLException {
        stmt.setNClob(parameterName, reader);
    }

    @Override
    public void setRowId(int parameterIndex, RowId x) throws SQLException {
        stmt.setRowId(parameterIndex, x);
    }

    @Override
    public void setNString(int parameterIndex, String value) throws SQLException {
        stmt.setNString(parameterIndex, value);
    }

    @Override
    public void setNCharacterStream(int parameterIndex, Reader value, long length) throws SQLException {
        stmt.setNCharacterStream(parameterIndex, value, length);
    }

    @Override
    public void setNClob(int parameterIndex, NClob value) throws SQLException {
        stmt.setNClob(parameterIndex, value);
    }

    @Override
    public void setClob(int parameterIndex, Reader reader, long length) throws SQLException {
        stmt.setClob(parameterIndex, reader, length);
    }

    @Override
    public void setBlob(int parameterIndex, InputStream inputStream, long length) throws SQLException {
        stmt.setBlob(parameterIndex, inputStream, length);
    }

    @Override
    public void setNClob(int parameterIndex, Reader reader, long length) throws SQLException {
        stmt.setNClob(parameterIndex, reader, length);
    }

    @Override
    public void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException {
        stmt.setSQLXML(parameterIndex, xmlObject);
    }

    @Override
    public void setAsciiStream(int parameterIndex, InputStream x, long length) throws SQLException {
        stmt.setAsciiStream(parameterIndex, x, length);
    }

    @Override
    public void setBinaryStream(int parameterIndex, InputStream x, long length) throws SQLException {
        stmt.setBinaryStream(parameterIndex, x, length);
    }

    @Override
    public void setCharacterStream(int parameterIndex, Reader reader, long length) throws SQLException {
        stmt.setCharacterStream(parameterIndex, reader, length);
    }

    @Override
    public void setAsciiStream(int parameterIndex, InputStream x) throws SQLException {
        stmt.setAsciiStream(parameterIndex, x);
    }

    @Override
    public void setBinaryStream(int parameterIndex, InputStream x) throws SQLException {
        stmt.setBinaryStream(parameterIndex, x);
    }

    @Override
    public void setCharacterStream(int parameterIndex, Reader reader) throws SQLException {
        stmt.setCharacterStream(parameterIndex, reader);
    }

    @Override
    public void setNCharacterStream(int parameterIndex, Reader value) throws SQLException {
        stmt.setNCharacterStream(parameterIndex, value);
    }

    @Override
    public void setClob(int parameterIndex, Reader reader) throws SQLException {
        stmt.setClob(parameterIndex, reader);
    }

    @Override
    public void setBlob(int parameterIndex, InputStream inputStream) throws SQLException {
        stmt.setBlob(parameterIndex, inputStream);
    }

    @Override
    public void setNClob(int parameterIndex, Reader reader) throws SQLException {
        stmt.setNClob(parameterIndex, reader);
    }

    @Override
    public boolean isClosed() throws SQLException {
        return stmt.isClosed();
    }

    @Override
    public void setPoolable(boolean poolable) throws SQLException {
        stmt.setPoolable(poolable);
    }

    @Override
    public boolean isPoolable() throws SQLException {
        return stmt.isPoolable();
    }

    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        return stmt.unwrap(iface);
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return stmt.isWrapperFor(iface);
    }
}
